#ifndef _GAMESCREEN_H_
# define _GAMESCREEN_H_

# include "Utils\\ScreenManager.h"
# include "Utils\\ScreenFactory.h"
# include "Utils\\Surface.h"
# include "Elements\\Image.h"
#include "Elements\\CheckBox.h"

using namespace SDL::UTILS;
using namespace SDL::ELEMENTS;

#pragma region Features

struct Background
{
public:
	Surface Surface;
	Rect Position;

public:
	Background(Rect const& _rect)
		: Surface(SDL_CreateRGBSurface(SDL_HWSURFACE, _rect.w, _rect.h, 32, 0, 0, 0, 0)),
			Position(_rect.x, _rect.y)
	{
	}
};

struct Baballe
{
public:
	Image Image;
	Rect Directions;
	Uint8 Speed;

public:
	Baballe() : Image("baballe.png"), Directions(1, 1), Speed(2)
	{
		Image.surface->SetColorKey(0x00FF00FF);
	}

	~Baballe() {}

	void Move(SDL_Surface* _screen)
	{
		Sint16 x = this->Image.GetPosition().x;
		Sint16 y = this->Image.GetPosition().y;
		Sint16 offsetx = this->Speed * this->Directions.x;
		Sint16 offsety = this->Speed * this->Directions.y;

		if (x + offsetx <= 0 || x + 16 + offsetx >= _screen->w)
			this->Directions.x *= -1;
		if (y + offsety <= 0 || y + 16 + offsety >= _screen->h)
			this->Directions.y *= -1;

		this->Image.Move(this->Speed * this->Directions.x,
						 this->Speed * this->Directions.y);
	}
};

#pragma endregion

class RebondScreen : public IScreen
{
private:
	Background m_background;
	Baballe m_baballe;

public:
	RebondScreen()
		: m_background(Rect(42, 42, 420, 420)), m_baballe()
	{
		m_baballe.Image.SetPosition(42);
		m_background.Surface.SetAlpha(128);
	}

	virtual ~RebondScreen() {}

	void Draw(IArgs* _args)
	{
		ScreenArgs* sa = (ScreenArgs*)_args;
		SDL_FillRect(m_background.Surface.surface, NULL, 0xFFFF00);
		m_baballe.Image.Draw(m_background.Surface.surface);
		m_background.Surface.BlitSurface(NULL, sa->Screen, &m_background.Position);
	}

	void Exec(IArgs* _args)
	{
		m_baballe.Move(m_background.Surface.surface);
	}

	static RebondScreen* Create(IArgs* _args = NULL) { return (new RebondScreen()); }
	static bool Func(ScreenManager::Element* _caller, IArgs* _args)
	{
		RebondScreen* rs = (RebondScreen*)_caller->Object;
		ScreenArgs* sa = (ScreenArgs*)_caller->Datas;
		if (sa->Pause == false)
			rs->Exec(_caller->Datas);
		if (sa->Hide == false)
			rs->Draw(_caller->Datas);
		return (true);
	}
};

class DefillementScreen : public IScreen
{
private:
	Background m_background;
	Baballe m_baballe;

public:
	DefillementScreen() : IScreen(), m_background(Rect(600, 0, 200, 600)), m_baballe()
	{
		m_baballe.Image.SetPosition(92);
		m_baballe.Directions.x = 0;
		m_baballe.Speed = 4;
	}

	virtual ~DefillementScreen() {}

	void Draw(IArgs* _args)
	{
		ScreenArgs* sa = (ScreenArgs*)_args;
		SDL_FillRect(m_background.Surface.surface, NULL, 0x00FFFF);
		m_baballe.Image.Draw(m_background.Surface.surface);
		m_background.Surface.BlitSurface(NULL, sa->Screen, &m_background.Position);
	}

	void Exec(IArgs* _args)
	{
		m_baballe.Move(m_background.Surface.surface);
	}

	static DefillementScreen* Create(IArgs* _args)
	{
		return (new DefillementScreen());
	}

	static bool Func(ScreenManager::Element* _caller, IArgs* _datas)
	{
		DefillementScreen* gs = (DefillementScreen*)_caller->Object;
		ScreenArgs* sa = (ScreenArgs*)_caller->Datas;
		if (sa->Pause == false)
			gs->Exec(_caller->Datas);
		if (sa->Hide == false)
			gs->Draw(_caller->Datas);
		return (true);
	}
};

class GameScreen : public IScreen
{
private:
	ScreenManager m_screenManager;
	Image m_background;
	Surface m_screen;
	bool m_backToMainScreen;
	Surface m_blackout;
	CheckBox m_cbHideAll;

public:
	GameScreen(IArgs* _args) : IScreen(), m_background("GameBackground.png"),
		m_screen(SDL_CreateRGBSurface(SDL_HWSURFACE, 800, 600, 32, 0, 0, 0, 0)),
		m_cbHideAll(L"Hide both subscreens")
	{
		ScreenArgs* sa = (ScreenArgs*)_args;
		m_screenManager.Add("RebondScreen", new RebondScreen(), RebondScreen::Func,
							new ScreenArgs(m_screen.surface, sa->Event));
		m_screenManager.Add("DefilScreen", new DefillementScreen(),
							DefillementScreen::Func,
							new ScreenArgs(m_screen.surface, sa->Event));
		m_cbHideAll.SetPosition(10, 10);
	}

	virtual ~GameScreen()
	{
	}

	void Draw(IArgs* _args)
	{
		ScreenArgs* sa = (MainArgs*)_args;
		SDL_FillRect(m_screen.surface, NULL, 0);
		m_background.Draw(m_screen.surface);

		m_screenManager.Manage();
		m_cbHideAll.Draw(m_screen.surface);

		m_screen.BlitSurface(NULL, sa->Screen, NULL);
	}

	void Exec(IArgs* _args)
	{
		static bool lastState = false;
		ScreenArgs* sa = (MainArgs*)_args;
		if (sa->Event->type == SDL_KEYDOWN)
		{
			if (sa->Event->key.keysym.sym == SDLK_ESCAPE)
				m_backToMainScreen = true;
			else if (sa->Event->key.keysym.sym == SDLK_p)
			{
				ScreenArgs* sa =
					(ScreenArgs*)m_screenManager.GetElement("RebondScreen")->Datas;
				sa->Pause = !sa->Pause;
			}
			else if (sa->Event->key.keysym.sym == SDLK_h)
			{
				ScreenArgs* sa =
					(ScreenArgs*)m_screenManager.GetElement("DefilScreen")->Datas;
				sa->Hide = !sa->Hide;
			}
			sa->Event->type = SDL_KEYUP;
		}
		m_cbHideAll.UpdateState();
		if (m_cbHideAll.IsChecked())
		{
			ScreenArgs* sa1 =
				(ScreenArgs*)m_screenManager.GetElement("RebondScreen")->Datas;
			ScreenArgs* sa2 =
				(ScreenArgs*)m_screenManager.GetElement("DefilScreen")->Datas;
			sa1->Hide = true;
			sa2->Hide = true;
			lastState = true;
		}
		else if (!m_cbHideAll.IsChecked() && m_cbHideAll.IsChecked() != lastState)
		{
			lastState = false;
			ScreenArgs* sa1 =
				(ScreenArgs*)m_screenManager.GetElement("RebondScreen")->Datas;
			ScreenArgs* sa2 =
				(ScreenArgs*)m_screenManager.GetElement("DefilScreen")->Datas;
			if (sa1->Hide == true)
				sa1->Hide = false;
			if (sa2->Hide == true)
				sa2->Hide = false;
		}
	}

	bool BackToScreen() const { return (m_backToMainScreen); }

	static GameScreen* Create(IArgs* _args) { return (new GameScreen(_args)); }
	static bool Func(ScreenManager::Element* _caller, IArgs* _args)
	{
		GameScreen* gs = (GameScreen*)_caller->Object;
		ScreenArgs* sa = (ScreenArgs*)_caller->Datas;
		if (sa->Pause == false)
			gs->Exec(_caller->Datas);
		if (sa->Hide == false)
			gs->Draw(_caller->Datas);
		return (true);
	}
};

#endif // _GAMESCREEN_H_
